#include <QDebug>
//#include <QRandomGenerator>
#include "qsnake.h"

#include <random>

// 生成一个指定范围的随机整数
int getRandomNumber(int min, int max)
{
    std::random_device rd;
    std::mt19937 rng(rd());
    std::uniform_int_distribution<int> uni(min, max);
    return uni(rng);
}

// 根据步长生成合适的随机坐标
int getRandomCoordinate(int areaStart, int areaSize, int step)
{
    int rangeStart = areaStart / step;
    int rangeEnd = (areaStart + areaSize) / step;
    int randomNumber = getRandomNumber(rangeStart, rangeEnd);
    return randomNumber * step;
}




PlaySnake::PlaySnake(QWidget *parent)
{
    this->parent = parent;
    g_speed = 10;
    g_speedFlag=false;
    runing = true;
    pause = false;
    snake = nullptr;
    frog = nullptr;
    direct = Right;
    step = 10;
    area_x = 0;
    area_y = 0;


    if(parent != nullptr)
    {
        area_h = (parent->height()/step)*step;
        area_w = (parent->width()/step)*step;

    }
    else
    {
        area_h = (100/step)*step;
        area_w = (100/step)*step;
    }

}

void PlaySnake::setRect(int w, int h, int x, int y)
{
    area_w = (w/step)*step;
    area_h = (h/step)*step;
    area_x = (x/step)*step;
    area_y = (y/step)*step;
}

void PlaySnake::setDirection(PlaySnake::Direction indirect)
{
    //限定蛇不能后退
    bool flag = (direct == Up && indirect == Down) ||
            (direct == Left && indirect == Right)||
            (direct == Down && indirect == Up) ||
            (direct == Right && indirect == Left) ;
    if(flag)
        return;

    //最小步伐限定，每次改变方向前，要等待上一次移动完成
    //避免移动一步，方向改变两次，造成矛盾
    if(step_flag)
    {
        direct = indirect;
        step_flag = false;
    }
}

//采用蛇头变蛇体，蛇尾变蛇头，蛇尾前一个节点变蛇尾方式交替，实现蛇到移动
//实际上每次移动只操作了三个节点
void PlaySnake::move(int step_x, int step_y)
{
    if(snake->size() < 3)
        return;

    snakeNodePtr body = snake->at(snake->size()-2);
    snakeNodePtr head = snake->first();
    snakeNodePtr tail = snake->takeLast();


    QString tmph = head->style;
    QString tmpt = tail->style;
    QString tmpb = body->style;


    int x = head->x + step_x;
    int y = head->y + step_y;

    tail->x = x;
    tail->y = y;
    tail->type = snakeNode::head;
    tail->style = tmph;

    head->type = snakeNode::body;
    head->style = tmpb;

    body->type = snakeNode::tail;
    body->style = tmpt;


    emit updataNode(body->disp,tmpt,body->x,body->y,body->step);
    emit updataNode(tail->disp,tmph,tail->x,tail->y,tail->step);
    emit updataNode(head->disp,tmpb,head->x,head->y,head->step);

    snake->prepend(tail);

}
void PlaySnake::setStyle(QString style, snakeNode::nodetype nodetype)
{
    if(nodetype == snakeNode::nodetype::head)
    {
       emit updataStyle(snake->first()->disp,style);
       return;
    }

    if(nodetype == snakeNode::nodetype::tail)
    {
       emit updataStyle(snake->last()->disp,style);
       return;
    }

    foreach (snakeNodePtr var, *snake) {

        if(var->type == nodetype)
           emit updataStyle(var->disp,style);
    }

    return;
}

bool PlaySnake::eating()
{

    if(!frog)
        return false;

    snakeNodePtr head = snake->first();
    snakeNodePtr body = snake->at(snake->size()-2);
    snakeNodePtr tail = snake->last();

    int s_x = head->x;
    int s_y = head->y;

    int f_x = frog->x;
    int f_y = frog->y;

    //吃到青蛙把青蛙变成蛇尾
    if(s_x == f_x && s_y == f_y)
    {
        frog->style = tail->style;
        frog->type = tail->type;
        frog->x = tail->x;
        frog->y = tail->y;

        tail->type = body->type;
        tail->style = body->style;

        snake->append(frog);

        emit updataNode(tail->disp,tail->style,tail->x,tail->y,tail->step);
        emit updataNode(frog->disp,frog->style,frog->x,frog->y,frog->step);

        int x=0 , y = 0;
        if(creatFrog(x,y))
            emit eaten(x,y,step);
        else
            return false;

        return true;
    }
    else
    {
        return false;
    }


}

bool PlaySnake::isBody()
{
    snakeNodePtr head = snake->first();

    foreach (snakeNodePtr var, *snake) {

        if(var->type == snakeNode::head)
            continue;
        if(var->x == head->x && var->y == head->y)
            return true;
    }

    return false;
}

bool PlaySnake::isBody(int x , int y)
{
    foreach (snakeNodePtr var, *snake) {
        if(var->x == x && var->y == y)
            return true;
    }

    return false;
}
bool PlaySnake::isWall()
{
    if(snake->isEmpty())
        return false;
    snakeNodePtr first = snake->first();
    //snake右移和下移需要多+step,节点（正方形)左上角代表实际坐标x,y
    int x = direct == Direction::Right ? first->x+step : first->x;
    int y = direct == Direction::Down ? first->y+step : first->y;

    return x > area_w+area_x || y > area_h+area_y || x < area_x || y < area_y;
}

bool PlaySnake::isWall(int x , int y)
{
   return x > area_w+area_x || y > area_h+area_y || x < area_x || y < area_y;
}

bool PlaySnake::creatFrog(int &x, int &y)
{
    int a_w = (area_w/step);
    int a_h = (area_h/step);
    int count = 0;
    int area_size = a_w * a_h ;
    //用于标记区域内那些区域被sanke占用
    bool *area = new bool[area_size] ;

    //创建一个合法的frog坐标，无法创建返回false;
    do
    {
        //限定随机区域范围，主窗口区域大小，并且为step的整数倍
        // 使用新的随机数生成方式获取 x 和 y 坐标
        x = getRandomCoordinate(area_x, area_w, step);
        y = getRandomCoordinate(area_y, area_h, step);
        if(!isBody(x,y)) return true;

        //蛇身体已经占满区域时无法创建返回false
        int i = x/step;
        int j = y/step;
        if(!area[i*a_w+j*a_h])
        {
            area[i*a_w+j*a_h] = true;
            count++;
        }

    }while(count < area_size - 1);

    runing = false;
    delete [] area;
    emit snakeMsg(FULL_AREA);
    return false;
}

void PlaySnake::run()
{
    int tmpspeed = 0;
    int speed = 0;
    int x = 0;
    int y = 0;
    int t_s = 0;
    bool change = true;


    //创建初始位置青蛙
    if(creatFrog(x,y))
       emit eaten(x,y,step);

    while (runing) {

        QThread::msleep(5ll);
        if(++t_s > 50)
        {
            t_s = 0;
            change = !change;
            //让frog闪烁起来
            if(frog != nullptr)
                emit updataStyle(frog->disp,change?FROG_STYLE2:FROG_STYLE1);
        }
        //暂停
        if(pause) continue;
        //加速标志
        if(!g_speedFlag){

            tmpspeed = g_speed;
        }
        else {
            tmpspeed = g_speed/20;
        }

        if(++speed < tmpspeed)
            continue;
        speed = 0;

        x = 0;
        y = 0;

        if(snake->isEmpty())
            continue;
        //方向选择
        switch (direct) {
        case Direction::Up:
            y -= step;
            break;
        case Direction::Down:
            y += step;
            break;
        case Direction::Left:
            x -= step;
            break;
        case Direction::Right:
            x += step;
            break;
        }
        //移到
        move(x,y);
        emit snakeMsg(SNAKE_MOVE);
        step_flag = true;
        //判断是否吃掉食物
        if(eating()){
            emit snakeMsg(EATEN);
            continue;
        }

        //判断是否撞墙
        if(isWall())
            emit snakeMsg(IS_WALL);

        if(isBody())
            emit snakeMsg(IS_BODY);

    }
}
